/*****************************************************************************\
     Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
                This file is licensed under the Snes9x License.
   For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/

/***********************************************************************************
  SNES9X for Mac OS (c) Copyright John Stiles

  Snes9x for Mac OS X

  (c) Copyright 2001 - 2011  zones
  (c) Copyright 2002 - 2005  107
  (c) Copyright 2002         PB1400c
  (c) Copyright 2004         Alexander and Sander
  (c) Copyright 2004 - 2005  Steven Seeger
  (c) Copyright 2005         Ryan Vogt
  (c) Copyright 2019         Michael Donald Buckley
 ***********************************************************************************/


#include "snes9x.h"
#include "memmap.h"
#include "cheats.h"

#include "mac-prefix.h"
#include "mac-audio.h"
#include "mac-dialog.h"
#include "mac-os.h"
#include "mac-screenshot.h"
#include "mac-stringtools.h"
#include "mac-cheatfinder.h"

#define	kCFNumBytesPop				'Size'
#define	kCFViewModeRad				'Mode'
#define	kCFCompModePop				'Math'
#define kCFCompStoredRad			'RSto'
#define	kCFCompLastRad				'RLst'
#define	kCFCompThisRad				'RThs'
#define	kCFCompValueTxt				'CTxt'
#define	kCFSearchBtn				'BSea'
#define	kCFStoreValueBtn			'BSto'
#define	kCFWatchBtn					'BWat'
#define kCFDrawerBtn				'Drwr'
#define	kCFWatchAddrTxt				'WTxt'
#define	kCFRestoreBtn				'BRes'
#define	kCFRemoveBtn				'BRem'
#define	kCFAddEntryBtn				'BAdd'
#define kCFUserPane					'Pane'
#define	kCFSheetAddrTxt				'AEad'
#define	kCFSheetCurrentValueTxt		'AEcv'
#define	kCFSheetCheetValueTxt		'AEtx'
#define	kCFSheetDescriptionTxt		'AEde'
#define	kCFSheetAddBtn				'SHTa'
#define	kCFSheetCancelBtn			'SHTc'
#define	kCFListView					'List'
#define	kCFUpperViews				'UI_T'
#define	kCFLowerViews				'UI_B'

#define kEventScrollableScrollThere	'ESST'
#define kEventCheatFinderList		'ECFL'
#define kControlListLinePart		172

#define	MAIN_MEMORY_SIZE			0x20000

#define	kCheatFinderListViewClassID	CFSTR("com.snes9x.macos.snes9x.cheatfinder")

enum
{
	kCFHexadecimal = 1,
	kCFSignedDecimal,
	kCFUnsignedDecimal
};

enum
{
	kCFCompWithStored = 1,
	kCFCompWithLast,
	kCFCompWithThis
};

enum
{
	kCFSearchEqual = 1,
	kCFSearchNotEqual,
	kCFSearchGreater,
	kCFSearchGreaterOrEqual,
	kCFSearchLess,
	kCFSearchLessOrEqual
};

typedef struct
{
	IBNibRef		nibRef;
	WindowRef		main;
	WindowRef		sheet;
	WindowRef		drawer;
	HIViewRef		list;
	HIViewRef		scroll;
	EventHandlerRef	sEref;
	EventHandlerUPP	sUPP;
}	WindowData;

typedef struct
{
	HIViewRef		view;
	HIPoint			originPoint;
	HISize			lineSize;
	Boolean			inFocus;
}	ListViewData;

Boolean	cfIsWatching = false;

extern SCheatData	Cheat;

static UInt8		*cfStoredRAM;
static UInt8		*cfLastRAM;
static UInt8		*cfCurrentRAM;
static UInt8		*cfStatusFlag;
static UInt32		*cfAddress;

static SInt32		cfNumRows;
static SInt32		cfListSelection;

static SInt32		cfViewMode;
static SInt32		cfCompMode;
static SInt32		cfCompWith;
static UInt32		cfViewNumBytes;
static UInt32		cfWatchAddr;
static Boolean		cfIsNewGame;
static Boolean		cfIsStored;
static Boolean		cfDrawerShow;

static int			cfListAddrColumnWidth;
static char			cfWatchTextFormat[32];
static CTFontRef	cfListLineCTFontRef;
#ifdef MAC_TIGER_PANTHER_SUPPORT
static ATSUStyle	cfListLineATSUStyle;
#endif

static HIViewID		kCheatFinderListViewID = { 'CHET', 'FNDR' };

static void CheatFinderSearch (WindowData *);
static void CheatFinderRestoreList (WindowData *);
static void CheatFinderRemoveFromList (WindowData *);
static void CheatFinderAdjustButtons (WindowData *);
static void CheatFinderBuildResultList (void);
static void CheatFinderHandleAddEntryButton (WindowData *);
static void CheatFinderMakeValueFormat (char *);
static void CheatFinderAddEntry (SInt64, char *);
static void CheatFinderBeginAddEntrySheet (WindowData *);
static void CheatFinderEndAddEntrySheet (WindowData *);
static void CheatFinderListViewScrollToThere (float, ListViewData *);
static void CheatFinderListViewDraw (CGContextRef, HIRect *, ListViewData *);
static float CheatFinderListViewSanityCheck (float, ListViewData *);
static SInt64 CheatFinderReadBytes (UInt8 *, UInt32);
static SInt64 CheatFinderGetValueEditText (ControlRef);
static Boolean CheatFinderCompare (SInt64, SInt64);
static HIViewPartCode CheatFinderListViewFindPart (EventRef, ListViewData *, SInt32 *);


void InitCheatFinder (void)
{
	cfStoredRAM  = new UInt8 [MAIN_MEMORY_SIZE + 10];
	cfLastRAM    = new UInt8 [MAIN_MEMORY_SIZE + 10];
	cfCurrentRAM = new UInt8 [MAIN_MEMORY_SIZE + 10];
	cfStatusFlag = new UInt8 [MAIN_MEMORY_SIZE + 10];
	cfAddress    = new UInt32[MAIN_MEMORY_SIZE + 10];

	if (!cfStoredRAM || !cfLastRAM || !cfCurrentRAM || !cfStatusFlag || !cfAddress)
		QuitWithFatalError(@"cheatfinder 01");

	memset(cfCurrentRAM, 0x00, MAIN_MEMORY_SIZE + 10);

	cfViewMode     = kCFUnsignedDecimal;
	cfViewNumBytes = 2;
	cfCompMode     = kCFSearchEqual;
	cfCompWith     = kCFCompWithThis;

    cfListLineCTFontRef = CTFontCreateWithName(CFSTR("Lucida Sans Typewriter Regular"), 11.0f, NULL);
    if (cfListLineCTFontRef == NULL)
    {
        cfListLineCTFontRef = CTFontCreateWithName(CFSTR("Menlo"), 11.0f, NULL);
        if (cfListLineCTFontRef == NULL)
        {
            cfListLineCTFontRef = CTFontCreateWithName(CFSTR("Monaco"), 11.0f, NULL);
            if (cfListLineCTFontRef == NULL)
                QuitWithFatalError(@"cheatfinder 02");
        }
    }
}

void ResetCheatFinder (void)
{
	memset(cfStoredRAM,  0x00, MAIN_MEMORY_SIZE);
	memset(cfLastRAM,    0x00, MAIN_MEMORY_SIZE);
	memset(cfStatusFlag, 0xFF, MAIN_MEMORY_SIZE);

	cfWatchAddr  = 0;
	cfIsNewGame  = true;
	cfIsWatching = false;
	cfIsStored   = false;
	cfDrawerShow = false;

	CheatFinderMakeValueFormat(cfWatchTextFormat);
}

void DeinitCheatFinder (void)
{
    CFRelease(cfListLineCTFontRef);

	delete [] cfStoredRAM;
	delete [] cfLastRAM;
	delete [] cfCurrentRAM;
	delete [] cfStatusFlag;
	delete [] cfAddress;
}

void CheatFinder (void)
{
//    static HIObjectClassRef    cfListViewClass = NULL;
//
//    OSStatus                err;
//    HIViewRef                ctl;
//    HIViewID                cid;
//    char                    num[256];
//    WindowData                cf;
//    EventHandlerRef            wEref, pEref;
//    EventHandlerUPP            wUPP, pUPP;
//    EventTypeSpec            wEvents[] = { { kEventClassCommand,    kEventCommandProcess           },
//                                          { kEventClassCommand,    kEventCommandUpdateStatus      },
//                                          { kEventClassWindow,     kEventWindowClose              } },
//                            pEvents[] = { { kEventClassControl,    kEventControlDraw              } },
//                            cEvents[] = { { kEventClassHIObject,   kEventHIObjectConstruct        },
//                                          { kEventClassHIObject,   kEventHIObjectInitialize       },
//                                          { kEventClassHIObject,   kEventHIObjectDestruct         },
//                                          { kEventClassScrollable, kEventScrollableGetInfo        },
//                                          { kEventClassScrollable, kEventScrollableScrollTo       },
//                                          { kEventCheatFinderList, kEventScrollableScrollThere    },
//                                          { kEventClassControl,    kEventControlHitTest           },
//                                          { kEventClassControl,    kEventControlTrack             },
//                                          { kEventClassControl,    kEventControlValueFieldChanged },
//                                          { kEventClassControl,    kEventControlDraw              } };
//
//    if (!cartOpen)
//        return;
//
//    err = CreateNibReference(kMacS9XCFString, &(cf.nibRef));
//    if (err == noErr)
//    {
//        err = CreateWindowFromNib(cf.nibRef, CFSTR("CheatFinder"), &(cf.main));
//        if (err == noErr)
//        {
//            err = CreateWindowFromNib(cf.nibRef, CFSTR("CFDrawer"), &(cf.drawer));
//            if (err == noErr)
//            {
//                memcpy(cfCurrentRAM, Memory.RAM, MAIN_MEMORY_SIZE);
//                CheatFinderBuildResultList();
//
//                err = noErr;
//                if (!cfListViewClass)
//                    err = HIObjectRegisterSubclass(kCheatFinderListViewClassID, kHIViewClassID, 0, CheatFinderListViewHandler, GetEventTypeCount(cEvents), cEvents, NULL, &cfListViewClass);
//                if (err == noErr)
//                {
//                    HIObjectRef        hiObject;
//                    HIViewRef        userpane, scrollview, listview, imageview, root;
//                    HILayoutInfo    layoutinfo;
//                    HIRect            frame;
//                    HISize            minSize;
//                    CGImageRef        image;
//                    Rect            rct;
//                    float            pich;
//
//                    GetWindowBounds(cf.main, kWindowContentRgn, &rct);
//
//                    minSize.width  = (float) (rct.right  - rct.left);
//                    minSize.height = (float) (rct.bottom - rct.top );
//                    err = SetWindowResizeLimits(cf.main, &minSize, NULL);
//
//                    root = HIViewGetRoot(cf.main);
//                    cid.id = 0;
//                    cid.signature = kCFUserPane;
//                    HIViewFindByID(root, cid, &userpane);
//
//                    err = HIScrollViewCreate(kHIScrollViewOptionsVertScroll, &scrollview);
//                    HIViewAddSubview(userpane, scrollview);
//                    HIViewGetBounds(userpane, &frame);
//                    cfListAddrColumnWidth = (int) (frame.size.width * 0.4);
//                    frame.origin.y    += 16.0f;
//                    frame.size.height -= 16.0f;
//                    frame = CGRectInset(frame, 1.0f, 1.0f);
//                    HIViewSetFrame(scrollview, &frame);
//                    HIViewSetVisible(scrollview, true);
//                    cf.scroll = scrollview;
//
//                    layoutinfo.version = kHILayoutInfoVersionZero;
//                    HIViewGetLayoutInfo(scrollview, &layoutinfo);
//
//                    layoutinfo.binding.top.toView    = userpane;
//                    layoutinfo.binding.top.kind      = kHILayoutBindTop;
//                    layoutinfo.binding.bottom.toView = userpane;
//                    layoutinfo.binding.bottom.kind   = kHILayoutBindBottom;
//                    layoutinfo.binding.left.toView   = userpane;
//                    layoutinfo.binding.left.kind     = kHILayoutBindLeft;
//                    layoutinfo.binding.right.toView  = userpane;
//                    layoutinfo.binding.right.kind    = kHILayoutBindRight;
//                    HIViewSetLayoutInfo(scrollview, &layoutinfo);
//
//                    err = HIObjectCreate(kCheatFinderListViewClassID, NULL, &hiObject);
//                    listview = (HIViewRef) hiObject;
//                    HIViewAddSubview(scrollview, listview);
//                    SetControl32BitMinimum(listview, 1);
//                    SetControl32BitMaximum(listview, cfNumRows);
//                    SetControl32BitValue(listview, 1);
//                    HIViewSetVisible(listview, true);
//                    cf.list = listview;
//
//                    cid.signature = kCFNumBytesPop;
//                    HIViewFindByID(root, cid, &ctl);
//                    SetControl32BitValue(ctl, cfViewNumBytes);
//
//                    cid.signature = kCFViewModeRad;
//                    HIViewFindByID(root, cid, &ctl);
//                    SetControl32BitValue(ctl, cfViewMode);
//
//                    cid.signature = kCFCompModePop;
//                    HIViewFindByID(root, cid, &ctl);
//                    SetControl32BitValue(ctl, cfCompMode);
//
//                    if (cfIsNewGame || (!cfIsStored && (cfCompWith == kCFCompWithStored)))
//                        cfCompWith = kCFCompWithThis;
//
//                    cid.signature = kCFCompStoredRad;
//                    HIViewFindByID(root, cid, &ctl);
//                    SetControl32BitValue(ctl, cfCompWith == kCFCompWithStored);
//                    if (cfIsStored)
//                        ActivateControl(ctl);
//                    else
//                        DeactivateControl(ctl);
//
//                    cid.signature = kCFCompLastRad;
//                    HIViewFindByID(root, cid, &ctl);
//                    SetControl32BitValue(ctl, cfCompWith == kCFCompWithLast);
//                    if (!cfIsNewGame)
//                        ActivateControl(ctl);
//                    else
//                        DeactivateControl(ctl);
//
//                    cid.signature = kCFCompThisRad;
//                    HIViewFindByID(root, cid, &ctl);
//                    SetControl32BitValue(ctl, cfCompWith == kCFCompWithThis);
//
//                    cid.signature = kCFCompValueTxt;
//                    HIViewFindByID(root, cid, &ctl);
//                    SetEditTextCFString(ctl, CFSTR(""), false);
//                    err = SetKeyboardFocus(cf.main, ctl, kControlFocusNextPart);
//
//                    cid.signature = kCFWatchBtn;
//                    HIViewFindByID(root, cid, &ctl);
//                    SetControl32BitValue(ctl, cfIsWatching);
//
//                    cid.signature = kCFDrawerBtn;
//                    HIViewFindByID(root, cid, &ctl);
//                    SetControl32BitValue(ctl, cfDrawerShow);
//
//                    cid.signature = kCFWatchAddrTxt;
//                    HIViewFindByID(root, cid, &ctl);
//                    if (cfIsWatching)
//                    {
//                        sprintf(num, "%06lX", cfWatchAddr + 0x7E0000);
//                        SetStaticTextCStr(ctl, num, false);
//                    }
//                    else
//                        SetStaticTextCFString(ctl, CFSTR(""), false);
//
//                    CheatFinderAdjustButtons(&cf);
//
//                    pUPP = NewEventHandlerUPP(CheatFinderListFrameEventHandler);
//                    err = InstallControlEventHandler(userpane, pUPP, GetEventTypeCount(pEvents), pEvents, (void *) userpane, &pEref);
//
//                    wUPP = NewEventHandlerUPP(CheatFinderWindowEventHandler);
//                    err = InstallWindowEventHandler (cf.main,  wUPP, GetEventTypeCount(wEvents), wEvents, (void *) &cf,      &wEref);
//
//                    pich = (float) (IPPU.RenderedScreenHeight >> ((IPPU.RenderedScreenHeight > 256) ? 1 : 0));
//
//                    err = SetDrawerParent(cf.drawer, cf.main);
//                    err = SetDrawerOffsets(cf.drawer, 0.0f, (float) ((rct.bottom - rct.top) - (pich + 37)));
//
//                    image = CreateGameScreenCGImage();
//                    if (image)
//                    {
//                        err = HIImageViewCreate(image, &imageview);
//                        if (err == noErr)
//                        {
//                            HIViewFindByID(HIViewGetRoot(cf.drawer), kHIViewWindowContentID, &ctl);
//
//                            HIViewAddSubview(ctl, imageview);
//                            HIImageViewSetOpaque(imageview, false);
//                            HIImageViewSetScaleToFit(imageview, true);
//                            HIViewSetVisible(imageview, true);
//
//                            frame.origin.x = 8.0f;
//                            frame.origin.y = 8.0f;
//                            frame.size.width  = (float) SNES_WIDTH;
//                            frame.size.height = pich;
//                            HIViewSetFrame(imageview, &frame);
//                        }
//                    }
//
//                    MoveWindowPosition(cf.main, kWindowCheatFinder, true);
//                    ShowWindow(cf.main);
//
//                    if (cfDrawerShow)
//                        err = OpenDrawer(cf.drawer, kWindowEdgeDefault, false);
//
//                    err = RunAppModalLoopForWindow(cf.main);
//
//                    HideWindow(cf.main);
//                    SaveWindowPosition(cf.main, kWindowCheatFinder);
//
//                    err = RemoveEventHandler(pEref);
//                    DisposeEventHandlerUPP(pUPP);
//
//                    err = RemoveEventHandler(wEref);
//                    DisposeEventHandlerUPP(wUPP);
//
//                    if (image)
//                        CGImageRelease(image);
//                }
//
//                CFRelease(cf.drawer);
//            }
//
//            CFRelease(cf.main);
//        }
//
//        DisposeNibReference(cf.nibRef);
//
//        memcpy(cfLastRAM, Memory.RAM, MAIN_MEMORY_SIZE);
//        cfIsNewGame = false;
//    }
}

static SInt64 CheatFinderReadBytes (UInt8 *mem, UInt32 addr)
{
	switch (cfViewMode)
	{
		case kCFSignedDecimal:
		{
			switch (cfViewNumBytes)
			{
				case 1:	return ((SInt64) (SInt8)      mem[addr]);
				case 2:	return ((SInt64) (SInt16)    (mem[addr] | (mem[addr + 1] << 8)));
				case 4:	return ((SInt64) (SInt32)    (mem[addr] | (mem[addr + 1] << 8) | (mem[addr + 2] << 16) | (mem[addr + 3] << 24)));
				case 3:	return ((SInt64) (((SInt32) ((mem[addr] | (mem[addr + 1] << 8) | (mem[addr + 2] << 16)) << 8)) >> 8));
			}

			break;
		}

		case kCFUnsignedDecimal:
		case kCFHexadecimal:
		{
			switch (cfViewNumBytes)
			{
				case 1:	return ((SInt64) (UInt8)      mem[addr]);
				case 2:	return ((SInt64) (UInt16)    (mem[addr] | (mem[addr + 1] << 8)));
				case 3:	return ((SInt64) (UInt32)    (mem[addr] | (mem[addr + 1] << 8) | (mem[addr + 2] << 16)));
				case 4:	return ((SInt64) (UInt32)    (mem[addr] | (mem[addr + 1] << 8) | (mem[addr + 2] << 16) | (mem[addr + 3] << 24)));
			}

			break;
		}
	}

	return (0);
}


static SInt64 CheatFinderGetValueEditText (HIViewRef control)
{
	SInt64	result = 0;
	UInt32	uvalue;
	SInt32	svalue;
	char	num[256];

	GetEditTextCStr(control, num);
	if (num[0] == 0)
	{
		SetEditTextCFString(control, CFSTR("0"), true);
		return (0);
	}

	switch (cfViewMode)
	{
		case kCFSignedDecimal:
		{
			if (sscanf(num, "%ld", &svalue) == 1)
			{
				switch (cfViewNumBytes)
				{
					case 1:
					{
						if (svalue >  127)
						{
							svalue =  127;
							SetEditTextCFString(control, CFSTR("127"), true);
						}
						else
						if (svalue < -128)
						{
							svalue = -128;
							SetEditTextCFString(control, CFSTR("-128"), true);
						}

						break;
					}

					case 2:
					{
						if (svalue >  32767)
						{
							svalue =  32767;
							SetEditTextCFString(control, CFSTR("32767"), true);
						}
						else
						if (svalue < -32768)
						{
							svalue = -32768;
							SetEditTextCFString(control, CFSTR("-32768"), true);
						}

						break;
					}

					case 3:
					{
						if (svalue >  8388607)
						{
							svalue =  8388607;
							SetEditTextCFString(control, CFSTR("8388607"), true);
						}
						else
						if (svalue < -8388608)
						{
							svalue = -8388608;
							SetEditTextCFString(control, CFSTR("-8388608"), true);
						}

						break;
					}
				}
			}
			else
			{
				svalue = 0;
				SetEditTextCFString(control, CFSTR("0"), true);
			}

			result = (SInt64) svalue;

			break;
		}

		case kCFUnsignedDecimal:
		{
			if (sscanf(num, "%lu", &uvalue) == 1)
			{
				switch (cfViewNumBytes)
				{
					case 1:
					{
						if (uvalue > 255)
						{
							uvalue = 255;
							SetEditTextCFString(control, CFSTR("255"), true);
						}

						break;
					}

					case 2:
					{
						if (uvalue > 65535)
						{
							uvalue = 65535;
							SetEditTextCFString(control, CFSTR("65535"), true);
						}

						break;
					}

					case 3:
					{
						if (uvalue > 16777215)
						{
							uvalue = 16777215;
							SetEditTextCFString(control, CFSTR("16777215"), true);
						}

						break;
					}
				}
			}
			else
			{
				uvalue = 0;
				SetEditTextCFString(control, CFSTR("0"), true);
			}

			result = (SInt64) uvalue;

			break;
		}

		case kCFHexadecimal:
		{
			if (sscanf(num, "%lx", &uvalue) == 1)
			{
				switch (cfViewNumBytes)
				{
					case 1:
					{
						if (uvalue > 0xFF)
						{
							uvalue = 0xFF;
							SetEditTextCFString(control, CFSTR("FF"), true);
						}

						break;
					}

					case 2:
					{
						if (uvalue > 0xFFFF)
						{
							uvalue = 0xFFFF;
							SetEditTextCFString(control, CFSTR("FFFF"), true);
						}

						break;
					}

					case 3:
					{
						if (uvalue > 0xFFFFFF)
						{
							uvalue = 0xFFFFFF;
							SetEditTextCFString(control, CFSTR("FFFFFF"), true);
						}

						break;
					}
				}
			}
			else
			{
				uvalue = 0;
				SetEditTextCFString(control, CFSTR("0"), true);
			}

			result = (SInt64) uvalue;

			break;
		}
	}

	return (result);
}

static void CheatFinderSearch (WindowData *cf)
{
//    SInt64    cmpvalue;
//    UInt8    *mem;
//
//    if (cfCompWith == kCFCompWithThis)
//    {
//        HIViewRef    ctl;
//        HIViewID    cid = { kCFCompValueTxt, 0 };
//
//        HIViewFindByID(HIViewGetRoot(cf->main), cid, &ctl);
//        cmpvalue = CheatFinderGetValueEditText(ctl);
//
//        for (int i = 0; i < cfNumRows; i++)
//            if (!CheatFinderCompare(CheatFinderReadBytes(cfCurrentRAM, cfAddress[i]), cmpvalue))
//                cfStatusFlag[cfAddress[i]] = 0;
//    }
//    else
//    {
//        mem = (cfCompWith == kCFCompWithStored) ? cfStoredRAM : cfLastRAM;
//
//        for (int i = 0; i < cfNumRows; i++)
//            if (!CheatFinderCompare(CheatFinderReadBytes(cfCurrentRAM, cfAddress[i]), CheatFinderReadBytes(mem, cfAddress[i])))
//                cfStatusFlag[cfAddress[i]] = 0;
//    }
//
//    CheatFinderBuildResultList();
//
//    SetControl32BitMaximum(cf->list, cfNumRows);
//    SetControl32BitValue(cf->list, 1);
}

static Boolean CheatFinderCompare (SInt64 ramvalue, SInt64 cmpvalue)
{
	switch (cfCompMode)
	{
		case kCFSearchEqual:			return (ramvalue == cmpvalue);
		case kCFSearchNotEqual:			return (ramvalue != cmpvalue);
		case kCFSearchGreater:			return (ramvalue >  cmpvalue);
		case kCFSearchGreaterOrEqual:	return (ramvalue >= cmpvalue);
		case kCFSearchLess:				return (ramvalue <  cmpvalue);
		case kCFSearchLessOrEqual:		return (ramvalue <= cmpvalue);
	}

	return (false);
}

static void CheatFinderBuildResultList (void)
{
	cfNumRows = 0;

	for (int i = 0; i < MAIN_MEMORY_SIZE; i++)
	{
		if (cfStatusFlag[i] == 0xFF)
		{
			cfAddress[cfNumRows] = i;
			cfNumRows++;
		}
	}

	cfListSelection = 0;
}

static void CheatFinderAdjustButtons (WindowData *cf)
{
//    HIViewRef    ctl, root;
//    HIViewID    cid;
//
//    cid.id = 0;
//    root = HIViewGetRoot(cf->main);
//
//    if (cfNumRows > 0)
//    {
//        cid.signature = kCFAddEntryBtn;
//        HIViewFindByID(root, cid, &ctl);
//        ActivateControl(ctl);
//
//        cid.signature = kCFRemoveBtn;
//        HIViewFindByID(root, cid, &ctl);
//        ActivateControl(ctl);
//
//        cid.signature = kCFWatchBtn;
//        HIViewFindByID(root, cid, &ctl);
//        ActivateControl(ctl);
//    }
//    else
//    {
//        cid.signature = kCFAddEntryBtn;
//        HIViewFindByID(root, cid, &ctl);
//        DeactivateControl(ctl);
//
//        cid.signature = kCFRemoveBtn;
//        HIViewFindByID(root, cid, &ctl);
//        DeactivateControl(ctl);
//
//        if (!cfIsWatching)
//        {
//            cid.signature = kCFWatchBtn;
//            HIViewFindByID(root, cid, &ctl);
//            DeactivateControl(ctl);
//        }
//    }
}

static void CheatFinderRemoveFromList (WindowData *cf)
{
//    if (cfNumRows > 0)
//    {
//        cfStatusFlag[cfAddress[cfListSelection]] = 0;
//
//        if (cfNumRows == 1)
//        {
//            cfNumRows = 0;
//
//            SetControl32BitMaximum(cf->list, 0);
//            SetControl32BitValue(cf->list, 1);
//        }
//        else
//        {
//            for (int i = cfListSelection; i < cfNumRows - 1; i++)
//                cfAddress[i] = cfAddress[i + 1];
//
//            cfNumRows--;
//            if (cfListSelection >= cfNumRows)
//                cfListSelection = cfNumRows - 1;
//
//            SetControl32BitMaximum(cf->list, cfNumRows);
//            SetControl32BitValue(cf->list, cfListSelection + 1);
//        }
//    }
}

static void CheatFinderRestoreList (WindowData *cf)
{
//    memset(cfStatusFlag, 0xFF, MAIN_MEMORY_SIZE);
//    CheatFinderBuildResultList();
//
//    SetControl32BitMaximum(cf->list, cfNumRows);
//    SetControl32BitValue(cf->list, 1);
}

static void CheatFinderMakeValueFormat (char *text)
{
//    switch (cfViewMode)
//    {
//        case kCFSignedDecimal:
//        case kCFUnsignedDecimal:
//        {
//            strcpy(text, "%lld");
//            break;
//        }
//
//        case kCFHexadecimal:
//        {
//            sprintf(text, "%%0%lullX", cfViewNumBytes * 2);
//            break;
//        }
//    }
}

void CheatFinderDrawWatchAddr (void)
{
//    static char    code[256];
//
//    uint16        *basePtr;
//    int            len;
//
//    sprintf(code, cfWatchTextFormat, CheatFinderReadBytes(Memory.RAM, cfWatchAddr));
//
//    basePtr = GFX.Screen + 1;
//    len = strlen(code);
//
//    for (int i = 0; i < len; i++)
//    {
//        S9xDisplayChar(basePtr, code[i]);
//        basePtr += (8 - 1);
//    }
}

static void CheatFinderHandleAddEntryButton (WindowData *cf)
{
//    if (cfAddress[cfListSelection] > (0x20000 - cfViewNumBytes))
//        NSBeep();
//    else
//    if (Cheat.g.size() + cfViewNumBytes > MAC_MAX_CHEATS)
//        AppearanceAlert(kAlertCautionAlert, kS9xMacAlertCFCantAddEntry, kS9xMacAlertCFCantAddEntryHint);
//    else
//        CheatFinderBeginAddEntrySheet(cf);
}

static void CheatFinderBeginAddEntrySheet (WindowData *cf)
{
//    OSStatus        err;
//    HIViewRef        ctl, root;
//    HIViewID        cid;
//    UInt32            addr;
//    char            str[256], form[256];
//    EventTypeSpec    sEvents[] = { { kEventClassCommand, kEventCommandProcess      },
//                                  { kEventClassCommand, kEventCommandUpdateStatus } };
//
//    err = CreateWindowFromNib(cf->nibRef, CFSTR("CFAddEntry"), &(cf->sheet));
//    if (err == noErr)
//    {
//        addr = cfAddress[cfListSelection];
//
//        root = HIViewGetRoot(cf->sheet);
//        cid.id = 0;
//
//        cid.signature = kCFSheetAddrTxt;
//        HIViewFindByID(root, cid, &ctl);
//        sprintf(str, "%06lX", addr + 0x7E0000);
//        SetStaticTextCStr(ctl, str, false);
//
//        cid.signature = kCFSheetCurrentValueTxt;
//        HIViewFindByID(root, cid, &ctl);
//        CheatFinderMakeValueFormat(form);
//        sprintf(str, form, CheatFinderReadBytes(cfCurrentRAM, addr));
//        SetStaticTextCStr(ctl, str, false);
//
//        cid.signature = kCFSheetCheetValueTxt;
//        HIViewFindByID(root, cid, &ctl);
//        SetEditTextCStr(ctl, str, false);
//
//        err = ClearKeyboardFocus(cf->sheet);
//        err = SetKeyboardFocus(cf->sheet, ctl, kControlFocusNextPart);
//
//        cid.signature = kCFSheetDescriptionTxt;
//        HIViewFindByID(root, cid, &ctl);
//        sprintf(str, "%06lX-%06lX", addr + 0x7E0000, addr + cfViewNumBytes - 1 + 0x7E0000);
//        SetStaticTextCStr(ctl, str, false);
//
//        cf->sUPP = NewEventHandlerUPP(CheatFinderSheetEventHandler);
//        err = InstallWindowEventHandler(cf->sheet, cf->sUPP, GetEventTypeCount(sEvents), sEvents, (void *) cf, &(cf->sEref));
//
//        err = ShowSheetWindow(cf->sheet, cf->main);
//    }
}

static void CheatFinderEndAddEntrySheet (WindowData *cf)
{
//    if (cf->sheet)
//    {
//        OSStatus    err;
//
//        err = HideSheetWindow(cf->sheet);
//
//        err = RemoveEventHandler(cf->sEref);
//        DisposeEventHandlerUPP(cf->sUPP);
//
//        CFRelease(cf->sheet);
//    }
}


static void CheatFinderAddEntry (SInt64 value, char *description)
{
	UInt32	addr, v;

	addr = cfAddress[cfListSelection];
	v = (UInt32) (SInt32) value;

	for (unsigned int i = 0; i < cfViewNumBytes; i++)
	{
		char code[10];
		snprintf(code, 10, "%x=%x", addr + i + 0x7E0000, (UInt8) ((v & (0x000000FF << (i * 8))) >> (i * 8)));
		int index = S9xAddCheatGroup(description, code);
		if(index >= 0)
			S9xEnableCheatGroup(index);
	}
}

static void CheatFinderListViewDraw (CGContextRef ctx, HIRect *bounds, ListViewData *myData)
{
//    static Boolean    init = true;
//
//    if (systemVersion >= 0x1050)
//    {
//        static CGRect            aRct, vRct;
//
//        CTLineRef                line;
//        CFDictionaryRef            attr;
//        CFAttributedStringRef    astr;
//        CFStringRef                str;
//        HIRect                    lineBounds;
//        SInt32                    start, end, val, max;
//        float                    ax, vx, y, f;
//        char                    format[32], t1[64], t2[64];
//
//        CFStringRef                keys[] = { kCTFontAttributeName, kCTForegroundColorAttributeName        };
//        CFTypeRef                bval[] = { cfListLineCTFontRef,  CGColorGetConstantColor(kCGColorBlack) },
//                                wval[] = { cfListLineCTFontRef,  CGColorGetConstantColor(kCGColorWhite) };
//
//        CheatFinderMakeValueFormat(format);
//
//        start = (SInt32)  (myData->originPoint.y / myData->lineSize.height);
//        end   = (SInt32) ((myData->originPoint.y + bounds->size.height) / myData->lineSize.height) + 1;
//
//        y = start * myData->lineSize.height - myData->originPoint.y;
//
//        lineBounds = *bounds;
//        lineBounds.size.height = myData->lineSize.height;
//        lineBounds.origin.y = y;
//
//        val = GetControl32BitValue(myData->view) - 1;
//        max = GetControl32BitMaximum(myData->view);
//
//        attr = CFDictionaryCreate(kCFAllocatorDefault, (const void **) &keys, (const void **) &bval, sizeof(keys) / sizeof(keys[0]), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
//
//        CGContextSetTextMatrix(ctx, CGAffineTransformIdentity);
//
//        if (init)
//        {
//            CGContextSetTextPosition(ctx, 0.0f, 0.0f);
//
//            astr = CFAttributedStringCreate(kCFAllocatorDefault, CFSTR("FFFFFF"), attr);
//            line = CTLineCreateWithAttributedString(astr);
//            aRct = CTLineGetImageBounds(line, ctx);
//            CFRelease(line);
//            CFRelease(astr);
//
//            astr = CFAttributedStringCreate(kCFAllocatorDefault, CFSTR("FFFFFFFFFFF"), attr);
//            line = CTLineCreateWithAttributedString(astr);
//            vRct = CTLineGetImageBounds(line, ctx);
//            CFRelease(line);
//            CFRelease(astr);
//
//            init = false;
//        }
//
//        ax = (float) (int) (((float) cfListAddrColumnWidth - 2.0 - aRct.size.width) / 2.0);
//        vx = (float) (int) (lineBounds.origin.x + lineBounds.size.width - vRct.size.width - 12.0);
//
//        for (int i = start; i <= end; i++)
//        {
//            if ((i == val) && cfNumRows)
//                CGContextSetRGBFillColor(ctx,  59.0f / 256.0f, 124.0f / 256.0f, 212.0f / 256.0f, 1.0f);
//            else
//            if ((i - start) % 2 == 0)
//                CGContextSetRGBFillColor(ctx, 256.0f / 256.0f, 256.0f / 256.0f, 256.0f / 256.0f, 1.0f);
//            else
//                CGContextSetRGBFillColor(ctx, 237.0f / 256.0f, 244.0f / 256.0f, 254.0f / 256.0f, 1.0f);
//
//            CGContextFillRect(ctx, lineBounds);
//
//            if (i < max)
//            {
//                CGContextScaleCTM(ctx, 1, -1);
//
//                if (i == val)
//                {
//                    CFRelease(attr);
//                    attr = CFDictionaryCreate(kCFAllocatorDefault, (const void **) &keys, (const void **) &wval, sizeof(keys) / sizeof(keys[0]), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
//                }
//
//                f = -(y + 12.0f);
//
//                sprintf(t1, "%06lX", cfAddress[i] + 0x7E0000);
//                str = CFStringCreateWithCString(kCFAllocatorDefault, t1, kCFStringEncodingUTF8);
//                astr = CFAttributedStringCreate(kCFAllocatorDefault, str, attr);
//                line = CTLineCreateWithAttributedString(astr);
//                CGContextSetTextPosition(ctx, ax, f);
//                CTLineDraw(line, ctx);
//                CFRelease(line);
//                CFRelease(astr);
//                CFRelease(str);
//
//                sprintf(t2, format, CheatFinderReadBytes(cfCurrentRAM, cfAddress[i]));
//                strcpy(t1, "            ");
//                t1[11 - strlen(t2)] = 0;
//                strcat(t1, t2);
//                str = CFStringCreateWithCString(kCFAllocatorDefault, t1, kCFStringEncodingUTF8);
//                astr = CFAttributedStringCreate(kCFAllocatorDefault, str, attr);
//                line = CTLineCreateWithAttributedString(astr);
//                CGContextSetTextPosition(ctx, vx, f);
//                CTLineDraw(line, ctx);
//                CFRelease(line);
//                CFRelease(astr);
//                CFRelease(str);
//
//                CGContextScaleCTM(ctx, 1, -1);
//
//                if (i == val)
//                {
//                    CFRelease(attr);
//                    attr = CFDictionaryCreate(kCFAllocatorDefault, (const void **) &keys, (const void **) &bval, sizeof(keys) / sizeof(keys[0]), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
//                }
//            }
//
//            y += myData->lineSize.height;
//            lineBounds.origin.y += myData->lineSize.height;
//        }
//
//        CFRelease(attr);
//    }
//#ifdef MAC_TIGER_PANTHER_SUPPORT
//    else
//    {
//        static Rect        aRect = { 0, 0, 0, 0 }, vRect = { 0, 0, 0, 0 };
//
//        OSStatus        err;
//        ATSUTextLayout    layout;
//        HIRect            lineBounds;
//        UniCharCount    runLength[1], len;
//        SInt32            start, end, val, max;
//        Fixed            ax, vx, f;
//        float            y;
//        UniChar            unistr[64];
//        char            format[32], t1[64], t2[64];
//
//        ATSUAttributeTag        theTags[]   = { kATSUCGContextTag    };
//        ByteCount                theSizes[]  = { sizeof(CGContextRef) };
//        ATSUAttributeValuePtr    theValues[] = { &ctx                 };
//
//        CheatFinderMakeValueFormat(format);
//
//        start = (SInt32)  (myData->originPoint.y / myData->lineSize.height);
//        end   = (SInt32) ((myData->originPoint.y + bounds->size.height) / myData->lineSize.height) + 1;
//
//        y = start * myData->lineSize.height - myData->originPoint.y;
//
//        lineBounds = *bounds;
//        lineBounds.size.height = myData->lineSize.height;
//        lineBounds.origin.y = y;
//
//        val = GetControl32BitValue(myData->view) - 1;
//        max = GetControl32BitMaximum(myData->view);
//
//        if (init)
//        {
//            f = Long2Fix(0);
//            for (unsigned int n = 0; n < 11; n++)
//                unistr[n] = 'F';
//
//            len = runLength[0] = 6;
//            err = ATSUCreateTextLayoutWithTextPtr(unistr, kATSUFromTextBeginning, kATSUToTextEnd, len, 1, runLength, &cfListLineATSUStyle, &layout);
//            err = ATSUSetLayoutControls(layout, sizeof(theTags) / sizeof(theTags[0]), theTags, theSizes, theValues);
//            err = ATSUMeasureTextImage(layout, kATSUFromTextBeginning, kATSUToTextEnd, f, f, &aRect);
//            err = ATSUDisposeTextLayout(layout);
//
//            len = runLength[0] = 11;
//            err = ATSUCreateTextLayoutWithTextPtr(unistr, kATSUFromTextBeginning, kATSUToTextEnd, len, 1, runLength, &cfListLineATSUStyle, &layout);
//            err = ATSUSetLayoutControls(layout, sizeof(theTags) / sizeof(theTags[0]), theTags, theSizes, theValues);
//            err = ATSUMeasureTextImage(layout, kATSUFromTextBeginning, kATSUToTextEnd, f, f, &vRect);
//            err = ATSUDisposeTextLayout(layout);
//
//            init = false;
//        }
//
//        ax = Long2Fix((cfListAddrColumnWidth - 2 - (aRect.right - aRect.left)) >> 1);
//        vx = Long2Fix((int) (lineBounds.origin.x + lineBounds.size.width) - (vRect.right - vRect.left) - 13);
//
//        for (int i = start; i <= end; i++)
//        {
//            if ((i == val) && cfNumRows)
//                CGContextSetRGBFillColor(ctx,  59.0f / 256.0f, 124.0f / 256.0f, 212.0f / 256.0f, 1.0f);
//            else
//            if ((i - start) % 2 == 0)
//                CGContextSetRGBFillColor(ctx, 256.0f / 256.0f, 256.0f / 256.0f, 256.0f / 256.0f, 1.0f);
//            else
//                CGContextSetRGBFillColor(ctx, 237.0f / 256.0f, 244.0f / 256.0f, 254.0f / 256.0f, 1.0f);
//
//            CGContextFillRect(ctx, lineBounds);
//
//            if (i < max)
//            {
//                CGContextScaleCTM(ctx, 1, -1);
//
//                if (i == val)
//                    CGContextSetRGBFillColor(ctx, 1.0f, 1.0f, 1.0f, 1.0f);
//                else
//                    CGContextSetRGBFillColor(ctx, 0.0f, 0.0f, 0.0f, 1.0f);
//
//                f = Long2Fix(-((int) y + 12));
//
//                sprintf(t1, "%06lX", cfAddress[i] + 0x7E0000);
//                len = runLength[0] = strlen(t1);
//                for (unsigned int n = 0; n < len; n++)
//                    unistr[n] = t1[n];
//                err = ATSUCreateTextLayoutWithTextPtr(unistr, kATSUFromTextBeginning, kATSUToTextEnd, len, 1, runLength, &cfListLineATSUStyle, &layout);
//                err = ATSUSetLayoutControls(layout, sizeof(theTags) / sizeof(theTags[0]), theTags, theSizes, theValues);
//                err = ATSUDrawText(layout, kATSUFromTextBeginning, kATSUToTextEnd, ax, f);
//                err = ATSUDisposeTextLayout(layout);
//
//                sprintf(t2, format, CheatFinderReadBytes(cfCurrentRAM, cfAddress[i]));
//                strcpy(t1, "            ");
//                t1[11 - strlen(t2)] = 0;
//                strcat(t1, t2);
//                len = runLength[0] = strlen(t1);
//                for (unsigned int n = 0; n < len; n++)
//                    unistr[n] = t1[n];
//                err = ATSUCreateTextLayoutWithTextPtr(unistr, kATSUFromTextBeginning, kATSUToTextEnd, len, 1, runLength, &cfListLineATSUStyle, &layout);
//                err = ATSUSetLayoutControls(layout, sizeof(theTags) / sizeof(theTags[0]), theTags, theSizes, theValues);
//                err = ATSUDrawText(layout, kATSUFromTextBeginning, kATSUToTextEnd, vx, f);
//                err = ATSUDisposeTextLayout(layout);
//
//                CGContextScaleCTM(ctx, 1, -1);
//            }
//
//            y += myData->lineSize.height;
//            lineBounds.origin.y += myData->lineSize.height;
//        }
//    }
//#endif
}

static HIViewPartCode CheatFinderListViewFindPart (EventRef inEvent, ListViewData *myData, SInt32 *whichLine)
{
//    OSStatus        err;
//    HIViewPartCode    part;
//    HIPoint            hipt;
//    SInt32            start, line;
//    float            y;
//
//    part = kControlNoPart;
//
//    start = (SInt32) (myData->originPoint.y / myData->lineSize.height);
//    y = start * myData->lineSize.height - myData->originPoint.y;
//
//    err = GetEventParameter(inEvent, kEventParamMouseLocation, typeHIPoint, NULL, sizeof(hipt), NULL, &hipt);
//    if (err == noErr)
//    {
//        line = start + (SInt32) ((hipt.y - y - 1) / myData->lineSize.height) + 1;
//
//        if (line <= GetControl32BitMaximum(myData->view))
//            part = kControlListLinePart;
//
//        if (whichLine != NULL)
//            *whichLine = line;
//    }
//
//    return (part);
    return 0;
}

static float CheatFinderListViewSanityCheck (float where, ListViewData *myData)
{
	HIRect	bounds;
	HISize	imageSize;

//    HIViewGetBounds(myData->view, &bounds);
//    imageSize = myData->lineSize;
//    imageSize.height *= GetControl32BitMaximum(myData->view);

	if (where + bounds.size.height > imageSize.height)
		where = imageSize.height - bounds.size.height;
	if (where < 0)
		where = 0;

	return (where);
}

static void CheatFinderListViewScrollToThere (float where, ListViewData *myData)
{
	OSStatus	err;
	EventRef	theEvent;
	HIPoint		whereP = { 0.0f, where };

	err = CreateEvent(kCFAllocatorDefault, kEventCheatFinderList, kEventScrollableScrollThere, GetCurrentEventTime(), kEventAttributeUserEvent, &theEvent);
	if (err == noErr)
	{
		err = SetEventParameter(theEvent, kEventParamOrigin, typeHIPoint, sizeof(whereP), &whereP);
//        if (err == noErr)
//            err = SendEventToEventTarget(theEvent, GetControlEventTarget(myData->view));

		ReleaseEvent(theEvent);
	}
}
